#!/usr/bin/python
# -*- coding: utf-8 -*-
import math
import random
import time

import pygame

import const


class Game:
	def __init__(self, screen, showScore, combo, decorate):
		self.imgList = []
		self.screen = screen
		self.showScore = showScore
		self.combo = combo
		self.decorate = decorate

	def update(self):
		self.check()
		for index, i in enumerate(self.imgList):
			self.imgList[index] = i.update(time.time(), self) if type(i) == Imgs else i.update()

	def add(self, pos, center, speed, degree):
		i = Imgs(pos, center, speed, degree, self.screen)
		self.imgList.append(i)
		if const.GameModel == 1:
			if 0 <= degree < 90:
				const.PressKeys['3'].append(i)
			elif 90 <= degree < 180:
				const.PressKeys['4'].append(i)
			elif 180 <= degree < 270:
				const.PressKeys['1'].append(i)
			elif 270 <= degree < 360:
				const.PressKeys['2'].append(i)
			else:
				const.PressKeys['3'].append(i)
		else:
			if 0 <= degree < 45:
				const.PressKeys['3'].append(i)
			elif 45 <= degree < 90:
				const.PressKeys['8'].append(i)
			elif 90 <= degree < 135:
				const.PressKeys['4'].append(i)
			elif 135 <= degree < 180:
				const.PressKeys['9'].append(i)
			elif 180 <= degree < 225:
				const.PressKeys['1'].append(i)
			elif 225 <= degree < 270:
				const.PressKeys['6'].append(i)
			elif 270 <= degree < 315:
				const.PressKeys['2'].append(i)
			elif 315 <= degree < 360:
				const.PressKeys['7'].append(i)
			else:
				const.PressKeys['3'].append(i)

	def press(self, key):
		if const.PressKeys[key]:
			d = const.PressKeys[key][0].distance()
			missed = const.MissedAfter if const.PressKeys[key][0].outRange() else const.MissedBefore
			if d < missed and not const.PressKeys[key][0].pressed:
				idx = int(d // (missed / len(const.Codes)))
				self.showScore.add(const.Codes[idx])
				const.PressKeys[key][0].pressed = True
				score = const.CodeScores[const.Codes[idx]]
				self.combo.add(score)

	def check(self):
		for i in self.imgList[::-1]:
			if not i.alive:
				self.imgList.remove(i)
				if type(i) == Imgs:
					degree = i.degree
					if const.GameModel == 1:
						if 0 <= degree < 90:
							const.PressKeys['3'].remove(i)
						elif 90 <= degree < 180:
							const.PressKeys['4'].remove(i)
						elif 180 <= degree < 270:
							const.PressKeys['1'].remove(i)
						elif 270 <= degree < 360:
							const.PressKeys['2'].remove(i)
						else:
							const.PressKeys['3'].remove(i)
					else:
						if 0 <= degree < 45:
							const.PressKeys['3'].remove(i)
						elif 45 <= degree < 90:
							const.PressKeys['8'].remove(i)
						elif 90 <= degree < 135:
							const.PressKeys['4'].remove(i)
						elif 135 <= degree < 180:
							const.PressKeys['9'].remove(i)
						elif 180 <= degree < 225:
							const.PressKeys['1'].remove(i)
						elif 225 <= degree < 270:
							const.PressKeys['6'].remove(i)
						elif 270 <= degree < 315:
							const.PressKeys['2'].remove(i)
						elif 315 <= degree < 360:
							const.PressKeys['7'].remove(i)
						else:
							const.PressKeys['3'].remove(i)

	def manualPress(self, enkey):
		# 按键 {a: 1, w: 2, d: 3, s: 4, left: 5, up: 6, right: 7, down: 8}
		if enkey not in const.KeyMaps:
			return
		key = const.KeyMaps[enkey]
		self.press(key)

	def autoPress(self):
		for j, keys in const.PressKeys.items():
			if not keys:
				continue
			d = keys[0].distance()
			if d <= 200:
				self.press(j)

	def addShows(self):
		if const.MusicNodeList and time.time() - const.StartTime >= const.MusicNodeList[0][1]:
			item = const.MusicNodeList.pop(0)
			if const.GameModel == 1:
				key = item[0] if int(item[0]) <= 4 else str(int(item[0])-5)
				degree = const.PressMustSimpleDict[key]
			elif const.GameModel == 2:
				degree = const.PressSimpleDict[item[0]]
			else:
				degree = random.randint(const.PressDict[item[0]][0], const.PressDict[item[0]][1])
			degree = degree % 360
			posX = 366 * math.cos(math.radians(degree)) + const.RectWidth
			posY = 366 * math.sin(math.radians(degree)) + const.RectHeight
			self.add([const.RectWidth, const.RectHeight], [posX, posY], item[2], degree)


class Combo:
	def __init__(self, screen):
		self.centers = [(const.ComboCenter[0] - 2 * const.ComboWidth, const.ComboCenter[1]),
						(const.ComboCenter[0] - const.ComboWidth, const.ComboCenter[1]),
						const.ComboCenter]
		self.updating = [False, False, False]
		self.speed = 5
		self.center = const.ComboCenter
		self.sufs = [pygame.Surface((const.ComboWidth, const.ComboHeight)).convert_alpha() for i in range(3)]
		self.combo = 0
		self.updatePool = [[], [], []]
		self.string = '--0'
		self.weight = const.ComboWidth
		self.height = const.ComboHeight
		self.rect = pygame.Rect(0, 0, const.ComboWidth, const.ComboHeight)
		self.screen = screen

	def _to_string(self):
		return '-' * (3 - len(str(self.combo))) + str(self.combo)

	def add(self, score):
		self.combo += score
		string = self._to_string()
		old = '-' if self.string[2] == '-' else int(self.string[2])
		self.updatePool[2].append([int(string[2]), old, self.height])
		if self.combo % 10 == 0 and self.combo > 8:
			old = '-' if self.string[1] == '-' else int(self.string[1])
			self.updatePool[1].append([string[1], old, self.height])
		if self.combo % 100 == 0 and self.combo > 88:
			old = '-' if self.string[0] == '-' else int(self.string[0])
			self.updatePool[0].append([string[0], old, self.height])
		self.string = string

	def miss(self):
		self.combo = 0
		self.string = '--0'

	def update(self):
		for i in range(3):
			self.sufs[i].fill((0, 0, 0, 0))
		if self.combo == 0:
			return
		for i, j in enumerate(self.updatePool):
			if not j:
				self.updating[i] = False
				if self.string[i] != '-':
					self.sufs[i].blit(const.MusicNumberList[int(self.string[i])], (0, 0))
				continue
			elif j and not self.updating[i]:
				self.updating[i] = True
			else:
				j[0][2] -= self.speed
				j[0][2] = j[0][2] if j[0][2] >= 0 else 0
				new, old = j[0][:2]
				self.sufs[i].blit(const.MusicNumberList[int(new)], (0, j[0][2]))
				if old != '-':
					self.sufs[i].blit(const.MusicNumberList[int(old)], (0, j[0][2] - self.height))
				if j[0][2] <= 0:
					self.updating[i] = False
					self.updatePool[i].pop(0)
		for i in range(3):
			self.rect.center = self.centers[i]
			self.screen.blit(self.sufs[i], self.rect)


class Score:
	def __init__(self, screen):
		self.imgList = []
		self.max = const.ScoreMax
		self.rect = pygame.Rect(const.ScoreRect)
		self.rect.center = (const.ScoreWidth, const.ScoreHeight)
		self.screen = screen

	def add(self, code):
		self.imgList.append([const.MusicScoreMap[code], 0])

	def update(self):
		if self.imgList:
			self.screen.blit(self.imgList[0][0], self.rect)
			self.imgList[0][1] += 1
			if self.imgList[0][1] >= self.max:
				self.imgList.pop(0)


class Decorate:
	def __init__(self, screen):
		self.count = 0
		self.max = len(const.MusicSliderList)
		self.rect = const.MusicSliderList[0].get_rect()
		self.rect.center = [const.DecorateWidth, const.DecorateHeight]
		self.screen = screen

	def update(self):
		self.screen.blit(const.MusicSliderList[self.count], self.rect)
		self.count = self.count + 1 if self.count < len(const.MusicSliderList) - 1 else 0


class Imgs:
	def __init__(self, pos, center, speed, degree, screen):
		self.startTime = time.time()
		self.imgList = random.choice(const.MusicCircleList)
		self.count = len(self.imgList) - 1
		self.count = 0
		self.speed = speed
		self.center = center
		self.degree = degree
		self.max = len(self.imgList)
		self.alive = True
		self.rect = self.imgList[0].get_rect()
		self.minx = True if pos[0] <= self.center[0] else False
		self.miny = True if pos[1] <= self.center[1] else False
		self.pressed = False
		self.pos = pos
		self.finish = pos.copy()
		self.screen = screen

	def distance(self):
		return math.pow(self.finish[0] - self.center[0], 2) + math.pow(self.finish[1] - self.center[1], 2)

	def outRange(self):
		return math.pow(self.finish[0] - const.RectWidth, 2) + math.pow(self.finish[1] - const.RectHeight, 2) > 366 * 366

	def update(self, t, game):
		if not self.alive:
			return
		img = self.imgList[self.count]
		if self.count < 10:
			self.count += 1
		if not self.pressed:
			posX = abs(self.center[0] - self.pos[0]) * (t - self.startTime) / self.speed
			posY = abs(self.center[1] - self.pos[1]) * (t - self.startTime) / self.speed
			posx = posX if self.minx else -posX
			x = self.pos[0] + posx
			posy = posY if self.miny else -posY
			y = self.pos[1] + posy
			self.finish = [x, y]
			# print(self.finish)
		else:
			x, y = self.finish
		d = math.pow(x - self.center[0], 2) + math.pow(y - self.center[1], 2)
		if d >= 1000 and not self.pressed and math.pow(x - const.RectWidth, 2) + math.pow(y - const.RectHeight, 2) > 366 * 366:
			game.imgList.append(Imgs2(self.center, self.screen))
			game.showScore.add(const.Scores[0])
			if const.GameMissReSet:
				game.combo.miss()
			self.alive = False
			return self
		if self.pressed:
			self.count = self.count + 1
		if self.count >= self.max - 1:
			self.alive = False
		self.rect.center = (round(x), round(y))
		self.screen.blit(img, self.rect)
		return self


class Imgs2:
	def __init__(self, pos, screen):
		self.imgList = const.MusicHitList
		self.count = 0
		self.max = len(const.MusicHitList)
		self.alive = True
		self.rect = self.imgList[0].get_rect()
		self.rect.center = pos
		self.screen = screen

	def update(self):
		if not self.alive:
			return self
		img = self.imgList[self.count]
		self.screen.blit(img, self.rect)
		self.count += 1
		if self.count >= self.max - 1:
			self.alive = False
		return self
